package com.dtflys.easyel.runtime;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author gongjun[jun.gong@thebeastshop.com]
 * @since v1.0.0
 */
public class MetaClassFactory {

    private final static Map<Class, MetaClass> cachedClasses = new HashMap<>();

    static {
        //                             byte, Byte, short, Short, int, Integer, long, Long, BigInteger, float, Float, double, Double, BigDecimal, Number, Object
        cachePrimitiveClass( byte.class,        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
        cachePrimitiveClass( Byte.class,        1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 );
        cachePrimitiveClass( short.class,       14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 );
        cachePrimitiveClass( Short.class,       14, 15, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 );
        cachePrimitiveClass( int.class,         14, 15, 12, 13, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 );
        cachePrimitiveClass( Integer.class,     14, 15, 12, 13, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 );
        cachePrimitiveClass( long.class,        14, 15, 12, 13, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 );
        cachePrimitiveClass( Long.class,        14, 15, 12, 13, 10, 11, 1, 0, 2, 3, 4, 5, 6, 7, 8, 9 );
        cachePrimitiveClass( BigInteger.class,  9, 10, 7, 8, 5, 6, 3, 4, 0, 14, 15, 12, 13, 11, 1, 2 );
        cachePrimitiveClass( float.class,       14, 15, 12, 13, 10, 11, 8, 9, 7, 0, 1, 2, 3, 4, 5, 6 );
        cachePrimitiveClass( Float.class,       14, 15, 12, 13, 10, 11, 8, 9, 7, 1, 0, 2, 3, 4, 5, 6 );
        cachePrimitiveClass( double.class,      14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 0, 1, 2, 3, 4 );
        cachePrimitiveClass( Double.class,      14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 1, 0, 2, 3, 4 );
        cachePrimitiveClass( BigDecimal.class,  14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 0, 1, 2 );
        cachePrimitiveClass( Number.class,      14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 2, 0, 1 );
        cachePrimitiveClass( Object.class,      14, 15, 12, 13, 10, 11, 8, 9, 7, 5, 6, 3, 4, 2, 1, 0 );

        registerMetaClass(char.class);
        registerMetaClass(Character.class);
        registerMetaClass(String.class);
        registerMetaClass(List.class);
        registerMetaClass(ArrayList.class);
        registerMetaClass(Map.class);
        registerMetaClass(HashMap.class);

        registerMetaClass(EasyElRange.class);
        registerMetaClass(EasyElDate.class);
        registerMetaClass(EasyElTimeDuration.class);

        initializeAllMetaClass();
    }

    public static MetaClass getMetaClass(Class clazz) {
        MetaClass cached = cachedClasses.get(clazz);
        if (cached == null) {
            synchronized (cachedClasses) {
                if (!cachedClasses.containsKey(clazz)) {
                    cached = new JavaBeanMetaClass(clazz);
                    cached.initialize();
                    cachedClasses.put(clazz, cached);
                }
            }
        }
        return cached;
    }

    public static void cachePrimitiveClass(Class clazz, int... primrayTypeDistanceTable) {
        PrimitiveMetaClass metaClass = new PrimitiveMetaClass(clazz, primrayTypeDistanceTable);
        cachedClasses.put(clazz, metaClass);
    }

    public static void registerMetaClass(Class clazz) {
        JavaBeanMetaClass metaClass = new JavaBeanMetaClass(clazz);
        cachedClasses.put(clazz, metaClass);
    }

    private static void initializeAllMetaClass() {
        for (MetaClass metaClass : cachedClasses.values()) {
            metaClass.initialize();
        }
    }
}
